package com.jiezuo.flowable.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jiezuo.common.enums.ActionEnum;
import com.jiezuo.common.exception.BusinessRuntimeException;
import com.jiezuo.common.util.ContextHolderUtils;
import com.jiezuo.common.util.DateTimeUtil;
import com.jiezuo.common.util.StringUtil;
import com.jiezuo.common.util.VerifyUtil;
import com.jiezuo.common.vo.PageVo;
import com.jiezuo.common.vo.Result;
import com.jiezuo.flowable.entity.*;
import com.jiezuo.flowable.service.MyService;
import com.jiezuo.flowable.service.UserTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.util.CollectionUtil;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @description 流程任务
 * @author 庞留杰
 * @date 2020/9/11 18:01
 * @version V1.0
 **/
@Slf4j
@Service
public class UserTaskServiceImpl implements UserTaskService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    MyService myService;


    @Override
    public Page backlog(PageVo pageVo, TaskBacklogQuery backlogQuery) {
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int firstResult = (current - 1) * size;// 第一条数据
        int maxResults = size;// 每页多少条
        TaskQuery query = taskService.createTaskQuery();
        if (StringUtil.isNotEmpty(backlogQuery.getUserId())) {
            query.taskCandidateUser(backlogQuery.getUserId());
        }
        if (StringUtil.isNotEmpty(backlogQuery.getBusinessKey())) {
            query.processInstanceBusinessKey(backlogQuery.getBusinessKey());
        }
        if (StringUtil.isNotEmpty(backlogQuery.getProcessInstanceId())) {
            query.processInstanceId(backlogQuery.getProcessInstanceId());
        }
        List<Task> list = query.listPage(firstResult, maxResults);
        Long count = query.count();
        Page page = new Page(current, size, count);
        page.setRecords(list);
        return page;
    }

    /**
     * 功能描述: 查询申请（查询我发起的流程实例）【对应表：act_hi_procinst 历史的流程实例表】
     * @author: 庞留杰
     * @param    taskDoingQuery
     * @return: Result
     * @exception
     * @date: 2020/9/16 17:44
     */
    @Override
    public Page doingPage(PageVo pageVo, TaskDoingQuery taskDoingQuery) {
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int firstResult = (current - 1) * size;// 第一条数据
        int maxResults = size;// 每页多少条
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();

        if (StringUtil.isNotEmpty(taskDoingQuery.getBusinessName())) {
            query.processInstanceNameLike("%" + taskDoingQuery.getBusinessName() + "%");
        }

        List<HistoricProcessInstance> historicProcessInstances = query.startedBy(taskDoingQuery.getUserId())
                .orderByProcessInstanceStartTime().desc() //开始时间倒序排列
                .listPage(firstResult, maxResults);
        JSONArray arr = new JSONArray();
        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        historicProcessInstances.forEach(historicProcessInstance -> {
            JSONObject obj = new JSONObject();
            obj.put("id", historicProcessInstance.getId());//流程实例ID:processInstanceId
            //执行实例ID（executionId）不返回，原因是：在多实例中，一个申请可能对应多个executionId，没意义
            obj.put("name", historicProcessInstance.getProcessDefinitionName());
            obj.put("processDefinitionId", historicProcessInstance.getProcessDefinitionId());
            obj.put("processDefinitionKey", historicProcessInstance.getProcessDefinitionKey());
            obj.put("processDefinitionName", historicProcessInstance.getProcessDefinitionName());
            obj.put("description", historicProcessInstance.getDescription());
            obj.put("startTime", DateTimeUtil.format(historicProcessInstance.getStartTime(), DateTimeUtil.DATE_TIME));
            obj.put("endTime", DateTimeUtil.format(historicProcessInstance.getEndTime(), DateTimeUtil.DATE_TIME));
            obj.put("durationInMillis", historicProcessInstance.getDurationInMillis()); //审核过程耗时
            obj.put("deleteReason", historicProcessInstance.getDeleteReason());//删除原因
            //发起人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(historicProcessInstance.getStartUserId())).findAny().orElse(null);
            if (map != null) {
                obj.put("startUserId", historicProcessInstance.getStartUserId());//发起人ID
                obj.put("startUserName", map.get("real_name"));//发起人
            } else {
                obj.put("startUserId", historicProcessInstance.getStartUserId());//发起人ID
                obj.put("startUserName", null);//发起人
            }
            HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicProcessInstance.getId()).singleResult();
            String businessKey = processInstance.getBusinessKey();
            String businessName = processInstance.getName();
            obj.put("businessKey", businessKey);//业务Key
            obj.put("businessName", businessName);//业务名称
            //结束状态 0：进行中，1：已结束
            if (historicProcessInstance.getEndTime() != null) {//说明已经结束
                obj.put("endState", "1");
            } else {
                obj.put("endState", "0");
            }
            arr.add(obj);
        });
        Long count = query.count();
        Page page = new Page(current, size, count);
        page.setRecords(arr);
        return page;
    }

    /**
     * 功能描述: 根据用户ID或者用户组ID，查询该用户代办，查询该用户代办数量
     * @author: 庞留杰
     * @param userId 用户ID
     * @param groupIds 用户组ID
     * @return:
     * @exception
     * @date: 2021/4/15 11:01
     */
    @Override
    public long count(String userId, List<String> groupIds) {
        long count = 0;
        if (CollectionUtil.isEmpty(groupIds)) {
            count = taskService.createTaskQuery().taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                    .orderByTaskCreateTime().asc()
                    .count();
        } else {
            count = taskService.createTaskQuery().taskCandidateOrAssigned(userId).taskCandidateGroupIn(groupIds).orderByTaskPriority().desc()
                    .orderByTaskCreateTime().asc()
                    .count();
        }
        return count;
    }

    /**
     * 功能描述: 根据用户ID或者用户组ID，查询该用户待办
     * @author: 庞留杰
     * @param query
     *   businessKey: ""
     *   businessName: ""
     *   businessType: ""
     *   current: 1
     *   groupIds: ["ROLE_ADMIN", "ROLE_GROUP_LEADER"]
     *   size: 20
     *   userId: "admin"
     * @return:
     * @exception
     * @date: 2021/4/15 11:01
     */
    @Override
    public Page todoPage(PageVo pageVo, TaskToDoQuery query) {
        // VerifyUtil.notEmpty("groupIds", query.getGroupIds());
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int firstResult = ((current - 1) * size);// 第一条数据
        int maxResults = size;// 每页多少条
        TaskQuery taskQuery = taskService.createTaskQuery();// 查询条件
        if (StringUtils.isNotBlank(query.getUserId())) {  //代办人ID
            taskQuery.taskCandidateOrAssigned(query.getUserId());//已经认领或分配给用户或等待用户
        }
        if (query.getGroupIds() != null) { //代办分组ID,例：["ROLE_ADMIN", "ROLE_GROUP_LEADER"]
            List<String> groupIds = Arrays.asList(query.getGroupIds());
            if (groupIds.size() > 0) {
                taskQuery.taskCandidateGroupIn(groupIds);
            }
        }
        if (StringUtils.isNotBlank(query.getBusinessKey())) {
            taskQuery.processInstanceBusinessKey(query.getBusinessKey());
        }
        if (StringUtils.isNotBlank(query.getProcessInstanceId())) {
            taskQuery.processInstanceId(query.getProcessInstanceId());
        }
        if (StringUtils.isNotBlank(query.getProcessDefinitionKey())) {
            taskQuery.processDefinitionKey(query.getProcessDefinitionKey());
        }
        //流程实例ID
        if (StringUtils.isNotBlank(query.getExecutionId())) {
            taskQuery.executionId(query.getExecutionId());
        }
        if (StringUtil.isNotEmpty(query.getBusinessName())) {
//            taskQuery.processVariableValueLike("businessName", query.getBusinessName());
//            taskQuery.taskNameLike("%" + query.getBusinessName() + "%");
        }

        /**
         * 查询
         * */
        List<Task> taskList = taskQuery
                //.includeProcessVariables()//在任务查询结果中包含全局任务变量(会导致慢查询：limitTaskVariables默认20000)
                .orderByTaskPriority().desc() //通过 TaskPriority 排序
                .orderByTaskCreateTime().desc() //通过 TaskCreateTime 排序
                .listPage(firstResult, maxResults);
        JSONArray arr = new JSONArray();
        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        taskList.forEach(task -> {
            JSONObject obj = new JSONObject();
            obj.put("id", task.getId());
            //  obj.put("assignee", task.getAssignee());//签收人或者委托人
            //代办人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(task.getAssignee())).findAny().orElse(null);
            if (map != null) {
                obj.put("assignee", task.getAssignee());//代办人ID
                obj.put("assigneeName", map.get("real_name"));//代办人
            } else {
                obj.put("assignee", task.getAssignee());//代办人ID
                obj.put("assigneeName", null);//代办人
            }
            obj.put("owner", task.getOwner());//（一般情况下为空，只有在委托时才有值）
            obj.put("name", task.getName());
            obj.put("variables", task.getProcessVariables());
            obj.put("createTime", DateTimeUtil.format(task.getCreateTime(), DateTimeUtil.DATE_TIME));
            obj.put("processInstanceId", task.getProcessInstanceId());//查询流程图
            obj.put("priority", task.getPriority());//优先级
            //isSuspended 如果流程实例被挂起，则返回true  （1挂起，2激活） 【对应act_ru_execution：SUSPENSION_STATE_】
            obj.put("isSuspended", task.isSuspended() == true ? 1 : 2);
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            String businessKey = processInstance.getBusinessKey();
            String businessName = processInstance.getName();
            String processDefinitionId = processInstance.getProcessDefinitionId();
            String processDefinitionKey = processInstance.getProcessDefinitionKey();
            String processDefinitionName = processInstance.getProcessDefinitionName();
            obj.put("businessKey", businessKey);
            obj.put("businessName", businessName);
            obj.put("processDefinitionId", processDefinitionId);
            obj.put("processDefinitionKey", processDefinitionKey);
            obj.put("processDefinitionName", processDefinitionName);
            obj.put("executionId", task.getExecutionId());//EXECUTION主键
            //  obj.put("startUserId", processInstance.getStartUserId());//流程发起人
            //发起人
            Map<String, Object> map1 = allUsers.stream().filter(e -> (e.get("id") + "").equals(processInstance.getStartUserId())).findAny().orElse(null);
            if (map1 != null) {
                obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                obj.put("startUserName", map1.get("real_name"));//发起人
            } else {
                obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                obj.put("startUserName", null);//发起人
            }
            arr.add(obj);
        });
        Long count = taskQuery.count();
        Page page = new Page(current, size, count);
        page.setRecords(arr);
        return page;
    }

    /**
     * 功能描述: 查询已办任务【对应表：act_hi_taskinst】
     * @author: 庞留杰
     * @param  taskDoneQuery
     * @return:
     * @exception
     * @date: 2020/9/14 9:14
     */
    @Override
    public Page donePage(PageVo pageVo, TaskDoneQuery taskDoneQuery) {
        String userId = taskDoneQuery.getUserId();
        String businessKey = taskDoneQuery.getBusinessKey();
        String businessName = taskDoneQuery.getBusinessName();
        String businessType = taskDoneQuery.getBusinessType();
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int firstResult = ((current - 1) * size);// 第一条数据
        int maxResults = size;// 每页多少条
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();
        if (StringUtils.isNotBlank(userId)) {
            query.taskAssignee(userId);
        }
        if (StringUtil.isNotEmpty(businessKey)) {
            query.processInstanceBusinessKey(businessKey + "");
        }
//        if (StringUtil.isNotEmpty(businessName)) {
//            query.processVariableValueLike("businessName", businessName);
//        }
        if (StringUtil.isNotEmpty(businessType)) {
            query.processVariableValueLike("businessType", businessType);
        }

        List<HistoricTaskInstance> historicTaskInstances = query.finished()
                // .includeProcessVariables()//在任务查询结果中包含全局任务变量(会导致慢查询：limitTaskVariables默认20000)
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .listPage(firstResult, maxResults);
        JSONArray arr = new JSONArray();
        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        historicTaskInstances.forEach(historicTaskInstance -> {
            JSONObject obj = new JSONObject();
            obj.put("id", historicTaskInstance.getId());
            obj.put("executionId", historicTaskInstance.getExecutionId());
            obj.put("processInstanceId", historicTaskInstance.getProcessInstanceId());
            obj.put("processDefinitionId", historicTaskInstance.getProcessDefinitionId());
            obj.put("taskDefinitionId", historicTaskInstance.getTaskDefinitionId());
            //代办人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(historicTaskInstance.getAssignee())).findAny().orElse(null);
            if (map != null) {
                obj.put("assignee", historicTaskInstance.getAssignee());//代办人ID
                obj.put("assigneeName", map.get("real_name"));//代办人
            } else {
                obj.put("assignee", historicTaskInstance.getAssignee());//代办人ID
                obj.put("assigneeName", null);//代办人
            }
            obj.put("createTime", DateTimeUtil.format(historicTaskInstance.getCreateTime(), DateTimeUtil.DATE_TIME));
            obj.put("endTime", DateTimeUtil.format(historicTaskInstance.getEndTime(), DateTimeUtil.DATE_TIME));
            obj.put("durationInMillis", historicTaskInstance.getDurationInMillis()); //审核过程耗时
            obj.put("name", historicTaskInstance.getName());
            obj.put("owner", historicTaskInstance.getOwner());//委托人（默认为空，只有在委托时才有值）
            //查询业务主键
            HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
            if (processInstance != null) {
                //发起人
                Map<String, Object> map1 = allUsers.stream().filter(e -> (e.get("id") + "").equals(processInstance.getStartUserId())).findAny().orElse(null);
                if (map1 != null) {
                    obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                    obj.put("startUserName", map1.get("real_name"));//发起人
                } else {
                    obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                    obj.put("startUserName", null);//发起人
                }
                obj.put("businessKey", processInstance.getBusinessKey());
                obj.put("businessName", processInstance.getName());
                obj.put("processDefinitionKey", processInstance.getProcessDefinitionKey());
                obj.put("processDefinitionName", processInstance.getProcessDefinitionName());
            }
            arr.add(obj);
        });
        long count = query.count();
        Page page = new Page(current, size, count);
        page.setRecords(arr);
        return page;
    }

    /**
     * 功能描述: 执行任务
     *  任务执行类型 claim：签收 unclaim 反签收 complete 完成 delegate 任务委派 resolve 任务签收完成 返回任务人 assignee 任务转办
     * @author: 庞留杰
     * @param taskExecuteQuery
     * {
     *          taskId:'',
     *          action:"complete",
     *          localScope:false,
     *          assignee:"1",
     *          variables:{}
     * }
     * @return:
     * @exception
     * @date: 2021/4/15 11:03
     */
    @Override
    public Map<String, Object> executeTask(TaskExecuteQuery taskExecuteQuery) {
        String taskId = taskExecuteQuery.getTaskId();
        String action = taskExecuteQuery.getAction();
        String userId = taskExecuteQuery.getAssignee();
        Boolean localScope = taskExecuteQuery.getLocalScope();
        Map<String, Object> variables = taskExecuteQuery.getVariables();
        log.info(">>>>>>>>>>>>>> 签收任务ID：{}--签收类型：{}--签收人ID：{} >>>>>>>>>>>>>>", taskId, action, userId);
        localScope = (null == localScope) ? false : localScope;
        Map<String, Object> result = Maps.newHashMap();
        ActionEnum actionEnum = ActionEnum.actionOf(action);
        switch (actionEnum) {
            case COMPLETE:
                //完成任务
                result = this.complete(taskId, variables, localScope);
                break;
            case CLAIM:
                //签收任务
                this.claim(taskId, userId);
                break;
            case UNCLAIM:
                //反签收
                this.unClaim(taskId);
                break;
            case DELEGATE:
                //任务委派
                this.delegate(taskId, userId);
                break;
            case RESOLVE:
                //委派任务完成，归还委派人
                this.resolveTask(taskId);
                break;
            case ASSIGNEE:
                //任务转办
                this.setAssignee(taskId, userId);
                break;
            default:
                break;
        }
        return result;
    }

    public void claim(String taskId, String userId) {
        log.info(">>>>>>>>>>>>>> 签收任务ID：{}--签收人：{} >>>>>>>>>>>>>>", taskId, userId);
        taskService.claim(taskId, userId);
    }

    public void unClaim(String taskId) {
        log.info(">>>>>>>>>>>>>> 反签收任务：{} >>>>>>>>>>>>>>", taskId);
        taskService.unclaim(taskId);
    }

    public void complete(String taskId) {
        this.complete(taskId, null);
        log.info(">>>>>>>>>>>>>> 任务ID：{},已完成 >>>>>>>>>>>>>>", taskId);
    }

    public void complete(String taskId, Map<String, Object> variables) {
        log.info(">>>>>>>>>>>>>> 完成任务ID：{} >>>>>>>>>>>>>>", taskId);
        taskService.complete(taskId, variables);
    }

    public Map<String, Object> complete(String taskId, Map<String, Object> variables, boolean localScope) {
        Task finishTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (finishTask == null) {
            throw new RuntimeException("通过taskId：" + taskId + "为查询到task");
        }
        /**complete()
         * 当任务成功执行时调用，并由最终用户提供所需的任务参数。
         *
         * @param taskId  要完成的任务的id不能为null。
         * @param variables 任务参数。可以为空。
         * @param localScope 如果为真，所提供的变量将存储在任务本地，而不是流程实例范围(这是{@link #complete(String, Map)}的默认值)。
         * @throws FlowableObjectNotFoundException 当给定id没有任务存在时。
         */
        try {
            taskService.complete(taskId, variables, localScope);
        } catch (Exception e) {
            //FlowableObjectNotFoundException:taskId 不存在
            //FlowableException  Unknown property used in expression: ${flag==2}
            throw new BusinessRuntimeException("工作流执行任务失败，原因：" + e.getMessage());
        }
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(finishTask.getProcessInstanceId()).active().list();
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> finish = new HashMap<>(16);
        finish.put("id", finishTask.getId());
        finish.put("name", finishTask.getName());
        map.put("finish", finish);// 刚刚执行结束的task信息
        List<Map<String, Object>> taskList = new ArrayList<>();
        for (Task task : tasks) {
            Map<String, Object> taskMap = new HashMap<>(16);
            taskMap.put("id", task.getId());
            taskMap.put("name", task.getName());
            taskMap.put("processInstanceId", task.getProcessInstanceId());
            taskMap.put("executionId", task.getExecutionId());
            taskList.add(taskMap);
        }
        map.put("active", taskList);// 即将执行的task信息
        return map;
    }

    public void setAssignee(String taskId, String userId) {
        log.info(">>>>>>>>>>>>>> 移交任务ID：{}--,移交给用户ID：{} >>>>>>>>>>>>>>", taskId, userId);
        taskService.setAssignee(taskId, userId);
    }

    public void delegate(String taskId, String userId) {
        log.info(">>>>>>>>>>>>>> 委派任务ID：{}--,委派给用户：{} >>>>>>>>>>>>>>", taskId, userId);
        taskService.delegateTask(taskId, userId);
    }

    public void resolveTask(String taskId) {
        log.info(">>>>>>>>>>>>>> 委派完成任务ID：{} >>>>>>>>>>>>>>", taskId);
        taskService.resolveTask(taskId);
    }

    public void delete(String taskId) {
        log.info(">>>>>>>>>>>>>> 删除任务：任务ID：{} >>>>>>>>>>>>>>", taskId);
        taskService.deleteTask(taskId);
    }

    /**
     * 功能描述: 查询任务
     * @author: 庞留杰
     * @param taskListQuery
     * @return:
     * @exception
     * @date: 2021/4/15 11:06
     */
    @Override
    public JSONArray findTask(TaskListQuery taskListQuery) {
        TaskQuery query = taskService.createTaskQuery();
        if (StringUtils.isNotBlank(taskListQuery.getTaskId())) {
            // 任务id查询单条记录
           /*  Task task = query.taskId(taskListQuery.getTaskId()).includeProcessVariables().singleResult();
            return task; */
        }
        if (StringUtils.isNotBlank(taskListQuery.getProcessInstanceId())) {
            query.processInstanceId(taskListQuery.getProcessInstanceId());
        }
        List<Task> tasks = query.includeProcessVariables().list();
        JSONArray arr = new JSONArray();
        tasks.forEach(task -> {
            JSONObject obj = new JSONObject();
            obj.put("id", task.getId());
            obj.put("name", task.getName());//taskName  合同主管审批
            obj.put("taskDefinitionKey", task.getTaskDefinitionKey());
            obj.put("description", task.getDescription());
            obj.put("executionId", task.getId());
            obj.put("processInstanceId", task.getProcessInstanceId());
            obj.put("processDefinitionId", task.getProcessDefinitionId());
            obj.put("createTime", DateTimeUtil.format(task.getCreateTime(), DateTimeUtil.DATE_TIME));
            obj.put("dueDate", DateTimeUtil.format(task.getDueDate(), DateTimeUtil.DATE_TIME));//用户任务的到期日期(dueDate)
            obj.put("owner", task.getOwner());
            obj.put("assignee", task.getAssignee());
            arr.add(obj);
        });
        return arr;
    }

    /**
     * @param commentAddQuery {
     *                        taskId: 任务ID
     *                        processInstanceId:流程实例ID
     *                        message: 批注信息
     *                        userId: 用户ID
     *                        }
     * @return
     * @description 添加备注信息
     * @author 庞留杰
     * @date 2020/9/14 9:34
     * @version V1.0
     */
    @Override
    public Result addComments(CommentAddQuery commentAddQuery) {
        String taskId = commentAddQuery.getTaskId();
        String processInstanceId = commentAddQuery.getProcessInstanceId();
        String message = commentAddQuery.getMessage();
        String userId = commentAddQuery.getUserId();
        log.info("----任务或者流程实例添加备注：任务ID:{},流程实例ID{}---------", taskId, processInstanceId);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("通过TaskId:【" + taskId + "】获取信息异常");
        }
        message = StringUtil.isNotEmpty(message) ? message : ""; //防止：当消息是null,flowable的AddCommentCmd方法会抛出异常
        Comment comment = taskService.addComment(taskId, processInstanceId, message);
        comment.setUserId(userId);
        taskService.saveComment(comment);
        return null;
    }

    /**
     * @description 添加备注信息 （不需要taskId）
     * @param  commentAddQuery
     * {
     *        processInstanceId:流程实例ID
     *        message: 批注信息
     *        userId: 用户ID
     * }
     *
     * ............ TODO:会签回退时存在问题
     * @author 庞留杰
     * @date 2020/9/14 9:34
     * @version V1.0
     **/
    @Override
    public void addComments2(CommentAddQuery commentAddQuery) {
        String processInstanceId = commentAddQuery.getProcessInstanceId();
        String message = commentAddQuery.getMessage();
        String userId = commentAddQuery.getUserId();
        log.info("----任务或者流程实例添加备注：流程实例ID{}---------", processInstanceId);
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if (list.size() == 0) {
            throw new BusinessRuntimeException("添加评论信息异常");
        }
        Task task = list.get(0);
        String taskId = task.getId();
        message = StringUtil.isNotEmpty(message) ? message : ""; //防止：当消息是null,flowable的AddCommentCmd方法会抛出异常
        Comment comment = taskService.addComment(taskId, processInstanceId, message);
        comment.setUserId(userId);
        taskService.saveComment(comment);
    }

    @Override
    public void addComments3(CommentAddQuery commentAddQuery) {
        String processInstanceId = commentAddQuery.getProcessInstanceId();
        String taskId = commentAddQuery.getTaskId();
        String message = commentAddQuery.getMessage();
        String userId = commentAddQuery.getUserId();
        log.info("----任务或者流程实例添加备注：流程实例ID{}---------", processInstanceId);
        message = StringUtil.isNotEmpty(message) ? message : ""; //防止：当消息是null,flowable的AddCommentCmd方法会抛出异常
        Comment comment = taskService.addComment(taskId, processInstanceId, message);
        comment.setUserId(userId);
        taskService.saveComment(comment);
    }


    /**
     * @description 查询备注信息
     * @param processInstanceId： 流程实例ID
     * @author 庞留杰
     * @date 2020/9/14 9:37
     * @version V1.0
     **/
    @Override
    public List<Comment> findCommentList(String processInstanceId) {
        List<Comment> comments = taskService.getProcessInstanceComments(processInstanceId);
        return comments;
    }

    /**
     * 功能描述: 获取所有可回退的节点
     * @author: 庞留杰
     * @param processInstanceId
     * @return:
     * @exception
     * @date: 2020/9/14 9:37
     */
    @Override
    public Map<String, Object> getFallbackNode(String processInstanceId) {
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = processInstance.getProcessDefinitionId();
        // 获取所有节点信息，暂不考虑子流程情况
        List<Process> processes = repositoryService.getBpmnModel(processDefinitionId).getProcesses();
        List<Map<String, String>> list = Lists.newArrayList();
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isNotEmpty(flowElements)) {
                for (FlowElement flowElement : flowElements) {
                    if (flowElement instanceof UserTask) {
                        Map<String, String> map = Maps.newHashMap();
                        //业务操作
                        map.put("id", flowElement.getId());
                        map.put("name", flowElement.getName());
                        list.add(map);
                    }
                    if (flowElement instanceof SubProcess) {
                        // ，，，
                    }
                }
            }
        }
        list.sort((a, b) -> a.get("id").compareTo(b.get("id")));//排序
        // 获取当前的task
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        /*if(task == null){
            throw new BusinessRuntimeException("通过ProcessInstanceId:【"+processInstanceId+"】获取信息异常，可能一下原因造成：1.流程实例ID错误，2：当前节点处于并行网关中，3.流程已结束");
        }*/
        list = list.stream().filter(it -> it.get("id").compareTo(task.getTaskDefinitionKey()) < 0).collect(Collectors.toList());//过滤
        Map<String, Object> map = Maps.newHashMap();
        map.put("history_nodes", list);
        if (task != null) {
            map.put("current_node", task.getTaskDefinitionKey());
            map.put("current_name", task.getName());
        }
        return map;
    }

    /**
     * 功能描述: 获取所有可回退的节点
     *          规则：
     *          1.从历史获取所有审批过的节点信息
     *          2.通过开始时间排序，去除历史中重复的节点
     * @author: 庞留杰
     * @param   processInstanceId
     * @return:
     * @exception
     * @date: 2021/1/21 15:10
     */
    @Override
    public Map<String, Object> getFallbackNode2(String processInstanceId) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);

        // 当前task,不支持并行网关
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        //        if(task == null){
        //            throw new BusinessRuntimeException("通过ProcessInstanceId:【"+processInstanceId+"】获取信息异常，可能一下原因造成：1.流程实例ID错误，2：当前节点处于并行网关中，3.流程已结束");
        //        }
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");// 可以过滤的值："startEvent", "endEvent", "userTask" ,"sequenceFlow"等
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceStartTime().asc().list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());

        List<Map<String, String>> list = Lists.newArrayList();
        collect.forEach(it -> {
            long count = list.stream().filter(item -> it.getActivityId().equals(item.get("id") + "")).count();
            if (count == 0 && !task.getTaskDefinitionKey().equals(it.getActivityId())) {//1.不存在2.不等于当前节点
                Map<String, String> m = Maps.newHashMap();
                //业务操作
                m.put("id", it.getActivityId());
                m.put("name", it.getActivityName());
                list.add(m);
            }
        });
        Map<String, Object> map = Maps.newHashMap();
        map.put("history_nodes", list);
        if (task != null) {
            map.put("current_node", task.getTaskDefinitionKey());
            map.put("current_name", task.getName());
        }
        return map;
    }

    /**
     * 功能描述: 获取所有可回退的节点
     *          规则：
     *              从历史获取所有审批过的节点信息
     *              带分页
     * @author: 庞留杰
     * @param   processInstanceId
     * @return:
     * @exception
     * @date: 2021/1/21 15:10
     */
    @Override
    public Map<String, Object> getFallbackNode3(String processInstanceId, PageVo pageVo) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int fromIndex = (current - 1) * size;// 每页多少条
        int toIndex = current * size;// 每页多少条
        // 当前task,不支持并行网关
        // Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        // 支持并行网关
        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId);
        if (ContextHolderUtils.currentUser() != null) {
            String userId = ContextHolderUtils.currentUser().getId();
            taskQuery.taskCandidateOrAssigned(userId);
            String group_ids = ContextHolderUtils.getFlowGroupIds();
            if (StringUtil.isNotEmpty(group_ids)) {
                String[] split = group_ids.split(",");
                List<String> groupIds = Arrays.asList(split);
                if (groupIds.size() > 0) {
                    taskQuery.taskCandidateGroupIn(groupIds);
                }
            }
        }
        List<Task> tasks = taskQuery.active().list();
        Task task = null;
        if (tasks.size() == 1) {
            task = tasks.get(0);
        } else {
            List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list();
            if (list.size() > 0) {
                task = list.get(0);
            }
            //task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        }
        //        if(task == null){
        //            throw new BusinessRuntimeException("获取信息异常，可能一下原因造成：1.流程实例ID错误，2：当前节点处于并行网关中，3.流程已结束");
        //        }
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");// 可以过滤的值："startEvent", "endEvent", "userTask" ,"sequenceFlow"等
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceStartTime().asc().list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        List<Map<String, Object>> list = Lists.newArrayList();
        int count = collect.size();
        if (toIndex > count) {
            toIndex = count;
        }
        collect = collect.subList(fromIndex, toIndex);

        // 走过的节点
        Map<String, String> nodes = new HashMap<>();
        for (HistoricActivityInstance his : collect) {
            nodes.put(his.getActivityId(), his.getActivityName());
        }

        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        collect.forEach(his -> {
            Map<String, Object> m = Maps.newHashMap();
            //业务操作
            m.put("id", his.getActivityId());
            m.put("name", his.getActivityName());
            //操作人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(his.getAssignee())).findAny().orElse(null);
            if (map != null) {
                m.put("assignee", his.getAssignee());//操作人ID
                m.put("assigneeName", map.get("real_name"));//操作人
            } else {
                m.put("assignee", his.getAssignee());//操作人ID
                m.put("assigneeName", null);//操作人
            }
            m.put("startTime", DateTimeUtil.format(his.getStartTime(), DateTimeUtil.DATE_TIME));
            m.put("endTime", DateTimeUtil.format(his.getEndTime(), DateTimeUtil.DATE_TIME));
            m.put("durationInMillis", his.getDurationInMillis());//耗时
            //备注信息
            List<Comment> taskComments = taskService.getTaskComments(his.getTaskId());
            List<Map> comments = Lists.newArrayList();
            taskComments.forEach(com -> {
                Map<String, Object> comment = Maps.newHashMap();
                comment.put("time", DateTimeUtil.format(com.getTime(), DateTimeUtil.DATE_TIME));
                comment.put("message", com.getFullMessage());
                comments.add(comment);
            });
            m.put("comments", comments);
            /**
             * 回退信息处理：
             * deleteReason:如果设置了任何删除原因，则返回此活动的删除原因(如果正常完成，则没有设置删除原因)
             *              回退原因：Change activity to sid-1
             * */
            String isRollback = "0", rollback = "", deleteReason = "";
            if (StringUtil.isNotEmpty(his.getDeleteReason()) && his.getDeleteReason().contains("Change activity to ")) {
                deleteReason = his.getDeleteReason();
                String sid = deleteReason.replace("Change activity to ", "").trim();
                String nodeName = nodes.get(sid);//回退到节点
                isRollback = "1";
                rollback = "由节点【" + his.getActivityName() + "】回退至节点【" + nodeName + "】";
            }
            m.put("isRollback", isRollback);//是否回退:0未回退，1已回退
            m.put("rollback", rollback);//回退原因
            m.put("deleteReason", deleteReason);//回退原因，英文
            list.add(m);
        });
        Page page = new Page(current, size, count);
        page.setRecords(list);
        page.setTotal(count);
        Map<String, Object> map = new HashMap<>();
        map.put("history_nodes", page);
        if (task != null) {
            map.put("current_node", task.getTaskDefinitionKey());
            map.put("current_name", task.getName());
        }
        return map;
    }

    @Override
    public Map<String, Object> getFallbackNode4(String processInstanceId, String taskId, PageVo pageVo) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int fromIndex = (current - 1) * size;// 每页多少条
        int toIndex = current * size;// 每页多少条
        Task task = null;
        if (StringUtil.isNotEmpty(taskId)) {
            // 支持并行网关
            task = taskService.createTaskQuery().taskId(taskId).singleResult();
        }
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");// 可以过滤的值："startEvent", "endEvent", "userTask" ,"sequenceFlow"等
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceStartTime().asc().list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        List<Map<String, Object>> list = Lists.newArrayList();
        int count = collect.size();
        if (toIndex > count) {
            toIndex = count;
        }
        collect = collect.subList(fromIndex, toIndex);

        // 走过的节点
        Map<String, String> nodes = new HashMap<>();
        for (HistoricActivityInstance his : collect) {
            nodes.put(his.getActivityId(), his.getActivityName());
        }

        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        collect.forEach(his -> {
            Map<String, Object> m = Maps.newHashMap();
            //业务操作
            m.put("id", his.getActivityId());
            m.put("name", his.getActivityName());
            //操作人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(his.getAssignee())).findAny().orElse(null);
            if (map != null) {
                m.put("assignee", his.getAssignee());//操作人ID
                m.put("assigneeName", map.get("real_name"));//操作人
            } else {
                m.put("assignee", his.getAssignee());//操作人ID
                m.put("assigneeName", null);//操作人
            }
            m.put("startTime", DateTimeUtil.format(his.getStartTime(), DateTimeUtil.DATE_TIME));
            m.put("endTime", DateTimeUtil.format(his.getEndTime(), DateTimeUtil.DATE_TIME));
            m.put("durationInMillis", his.getDurationInMillis());//耗时
            //备注信息
            List<Comment> taskComments = taskService.getTaskComments(his.getTaskId());
            List<Map> comments = Lists.newArrayList();
            taskComments.forEach(com -> {
                Map<String, Object> comment = Maps.newHashMap();
                comment.put("time", DateTimeUtil.format(com.getTime(), DateTimeUtil.DATE_TIME));
                comment.put("message", com.getFullMessage());
                comments.add(comment);
            });
            m.put("comments", comments);
            /**
             * 回退信息处理：
             * deleteReason:如果设置了任何删除原因，则返回此活动的删除原因(如果正常完成，则没有设置删除原因)
             *              回退原因：Change activity to sid-1
             * */
            String isRollback = "0", rollback = "", deleteReason = "";
            if (StringUtil.isNotEmpty(his.getDeleteReason()) && his.getDeleteReason().contains("Change activity to ")) {
                deleteReason = his.getDeleteReason();
                String sid = deleteReason.replace("Change activity to ", "").trim();
                String nodeName = nodes.get(sid);//回退到节点
                isRollback = "1";
                rollback = "由节点【" + his.getActivityName() + "】回退至节点【" + nodeName + "】";
            }
            // 多实例回退
            if (StringUtil.isNotEmpty(his.getDeleteReason()) && his.getDeleteReason().contains("Change parent activity to ")) {
                deleteReason = his.getDeleteReason();
                String sid = deleteReason.replace("Change parent activity to ", "").trim();
                String nodeName = nodes.get(sid);//回退到节点
                rollback = "#会签回退#由节点【" + his.getActivityName() + "】回退至节点【" + nodeName + "】";
            }
            m.put("isRollback", isRollback);//是否回退:0未回退，1已回退
            m.put("rollback", rollback);//回退原因
            m.put("deleteReason", deleteReason);//回退原因，英文
            list.add(m);
        });
        Page page = new Page(current, size, count);
        page.setRecords(list);
        page.setTotal(count);
        Map<String, Object> map = new HashMap<>();
        map.put("history_nodes", page);
        if (task != null) {
            map.put("current_node", task.getTaskDefinitionKey());
            map.put("current_name", task.getName());
        }
        return map;
    }


    /**
     * 功能描述: 任务撤回【注意：当前与目标定义Key为设计模板时任务对应的ID(通常格式：sid-xxxxxxxxxxxxxxxxx),而非数据主键ID】
     * @author: 庞留杰
     * @param taskFallbackQuery
     *         processInstanceId ：流程实例ID
     *         currentTaskKey ：当前任务定义Key
     *         targetTaskKey ：目标任务定义Key
     * @return:
     * @exception
     * @date: 2021/1/21 11:18
     */
    @Override
    public void withdraw(TaskFallbackQuery taskFallbackQuery) {
        VerifyUtil.notEmpty("processInstanceId", taskFallbackQuery.getProcessInstanceId());
        VerifyUtil.notEmpty("currentTaskKey", taskFallbackQuery.getCurrentTaskKey());
        VerifyUtil.notEmpty("targetTaskKey", taskFallbackQuery.getTargetTaskKey());
        log.info(">>>>>>>>>>>>>> 任务撤回：流程实例ID:{},当前活动任务ID:{},撤回到达的任务ID:{}，>>>>>>>>>>>>>>", taskFallbackQuery.getProcessInstanceId(), taskFallbackQuery.getCurrentTaskKey(), taskFallbackQuery.getTargetTaskKey());
        String processInstanceId = taskFallbackQuery.getProcessInstanceId();
        /*String source_act_id = taskFallbackQuery.getCurrentTaskKey();
        String target_act_id = taskFallbackQuery.getTargetTaskKey();*/
        try {
            /*String userId = ContextHolderUtils.currentUser().getId();
            List<Task> tasksBefore = taskService.createTaskQuery().processInstanceId(processInstanceId).list();*/
            // 对于（并行网关，包含网关，多实例）禁止回退
            prohibitFallback(taskFallbackQuery.getProcessInstanceId(), taskFallbackQuery.getTargetTaskKey());
            // 回退处理
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(processInstanceId)
                    //并行网关不能用
                    .moveActivityIdTo(taskFallbackQuery.getCurrentTaskKey(), taskFallbackQuery.getTargetTaskKey())
                    // 只回退一个执行实例
                    // .moveExecutionToActivityId(taskFallbackQuery.getCurrentTaskKey(), taskFallbackQuery.getTargetTaskKey())
                    .changeState();
            /*
            List<Task> tasksAfter = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            // 应对分支流程向主流程中回退时，记录新增的task
            for (Task task : tasksAfter) {
                Optional<Task> newTask = tasksBefore.stream().filter(it -> it.getId().equals(task.getId())).findFirst();
                if(!newTask.isPresent()){
                    //新生成的Task
                    myService.insertFallBack(processInstanceId,
                            task.getId(),
                            source_act_id,
                            target_act_id,
                            userId,
                            new Date(),
                            userId,
                            new Date(),
                            ""
                    );
                }
            }*/
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务撤回失败");
        }
    }

    //对于并行网关，包含网关，多实例 直接不允许回退（PLJ:2023年10月7日17:48:40，去掉这个限制）
    public void prohibitFallback(String processInstanceId, String targetTaskKey) {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (pi == null) {
            throw new BusinessRuntimeException("通过流程实例ID[" + processInstanceId + "]，未查到流程，请确认流程实例ID是否正确");
        }
        Process process = repositoryService.getBpmnModel(pi.getProcessDefinitionId()).getMainProcess();
        /* ParallelGateway并行网关
         *  InclusiveGateway 包容网关
         *  MultiInstanceActivityBehavior 多实例
         */
        // 通过flowElementId查询所有节点，包含子节点，如果searchRecursive=false，或者不传，不会查询子流程内的节点
        Activity targetFlowElement = (Activity) process.getFlowElement(targetTaskKey, true);
        if (targetFlowElement == null) {
            throw new BusinessRuntimeException("通过目标节点ID[" + targetTaskKey + "]未查到节点信息，请确认目标节点ID是否正确");
        }
        String targetName = targetFlowElement.getName();
        List<SequenceFlow> incomingFlows = targetFlowElement.getIncomingFlows();// 获取的值 (上个节点sid->当前节点的sid)
//        if (CollectionUtils.isNotEmpty(incomingFlows)) {
//            for (SequenceFlow sequenceFlow : incomingFlows) {
//                FlowElement upNode = sequenceFlow.getSourceFlowElement();
//                /***********************************并行网关*********************************/
//                if (upNode instanceof ParallelGateway) {// 并行网关
//                    String msg = String.format("回退的节点“%s”处于并行网关中，不允许回退，请选择其他节点回退", targetName);
//                    throw new BusinessRuntimeException(msg);
//                }
//                /***********************************包容网关*********************************/
//                if (upNode instanceof InclusiveGateway) {// 包容网关
//                    String msg = String.format("回退的节点“%s”处于包容网关中，不允许回退，请选择其他节点回退", targetName);
//                    throw new BusinessRuntimeException(msg);
//                }
//            }
//        }
        /***********************************多实例*********************************/
        if (isSubprocessActive(process, targetTaskKey)) {//当前节点在子流程内部
            if (targetFlowElement instanceof UserTask) {//只是普通节点
                FlowElementsContainer parentContainer = targetFlowElement.getParentContainer();
                Activity activity = (Activity) parentContainer;
                if (activity.getBehavior() instanceof MultiInstanceActivityBehavior) {//判断父节点是否是多实例
                    String msg = String.format("回退的节点“%s”，处于多实例的子流程中，不允许回退，请选择其他节点回退", targetName);
                    throw new BusinessRuntimeException(msg);
                }
            } else {
                if (targetFlowElement != null && targetFlowElement.getBehavior() instanceof MultiInstanceActivityBehavior) {//多实例
                    String msg = String.format("回退的节点“%s”属于多实例节点，不允许回退，请选择其他节点回退", targetName);
                    throw new BusinessRuntimeException(msg);
                }
            }
        } else {
            if (!(targetFlowElement instanceof UserTask)) {//不是普通节点
                if (targetFlowElement != null && targetFlowElement.getBehavior() instanceof MultiInstanceActivityBehavior) {//多实例
                    String msg = String.format("回退的节点“%s”属于多实例节点，不允许回退，请选择其他节点回退", targetName);
                    throw new BusinessRuntimeException(msg);
                }
            }
        }
    }

    // 判断是否是子流程中的节点:false:不是，true:是
    public boolean isSubprocessActive(Process process, String activityId) {
        FlowElement flowElement1 = process.getFlowElement(activityId, false);
        FlowElement flowElement2 = process.getFlowElement(activityId, true);
        if (flowElement1 == null && flowElement2 == null) {
            throw new BusinessRuntimeException(String.format("通过activityId:%s，查询不到该节点", activityId));
        }
        if (flowElement1 != null) {
            return false;
        } else {
            return true;
        }
    }


    /**
     * 功能描述: 退回上一步，注意只能回退一步【例： 当前在“第三步”：
     *                              调用一次，回退到“第二步”，
     *                              再次调用，回退到“第三步”，
     *                              再次调用，回退到“第二步”，
     *                                以此类推。。。。
     *           】
     * @author: 庞留杰
     * @param processInstanceId：流程实例ID
     * @return:
     * @exception
     * @date: 2021/1/21 11:20
     */
    @Override
    public Map<String, String> rollback(String processInstanceId) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("流程实例processInstanceId：" + processInstanceId + "，不正确");
        }
        String currentTaskKey = task.getTaskDefinitionKey();//当前节点id
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().desc().list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        String targetTaskKey = collect.get(0).getActivityId();//审批历史中上一个节点id
        log.info(">>>>>>>>>>>>> 任务回退：流程实例ID:{}，当前审批节点-ID:{}-NAME:{}，回退节点ID:{}-NAME:{} >>>>>>>>>>>>>",
                processInstanceId,
                task.getTaskDefinitionKey(), task.getName(),
                collect.get(0).getActivityId(),
                collect.get(0).getActivityName());
        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        Map<String, String> map = Maps.newHashMap();
        map.put("from", currentTaskKey);//从哪个id开始回退
        map.put("fromName", task.getName());//从哪个id开始回退
        map.put("to", targetTaskKey);//回退到哪个id
        map.put("toName", collect.get(0).getActivityName());//回退到哪个id
        return map;
    }

    @Override
    public Map<String, String> rollback2(Map<String, Object> map) {
        VerifyUtil.notEmpty("processInstanceId", map.get("processInstanceId"));
        VerifyUtil.notEmpty("previousActivityId", map.get("previousActivityId"));
        String processInstanceId = map.get("processInstanceId") + "";
        String previousActivityId = map.get("previousActivityId") + "";
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("流程实例processInstanceId：" + processInstanceId + "，不正确");
        }
        String currentTaskKey = task.getTaskDefinitionKey();//当前节点id
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().desc().list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        String targetTaskKey = collect.get(0).getActivityId();//审批历史中上一个节点id
        if (!previousActivityId.equals(targetTaskKey)) {
            throw new BusinessRuntimeException("当前流程处于【" + task.getName() + "】，不允许回收");
        }
        log.info(">>>>>>>>>>>>> 任务回退：流程实例ID:{}，当前审批节点-ID:{}-NAME:{}，回退节点ID:{}-NAME:{} >>>>>>>>>>>>>",
                processInstanceId,
                task.getTaskDefinitionKey(), task.getName(),
                collect.get(0).getActivityId(),
                collect.get(0).getActivityName());
        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        Map<String, String> result = Maps.newHashMap();
        result.put("from", currentTaskKey);//从哪个id开始回退
        result.put("to", targetTaskKey);//回退到哪个id
        return result;
    }


    /**
     * 功能描述: 依次向前回退【例： 当前在“第三步”：
     *                              调用一次，回退到“第二步”，
     *                              再次调用，回退到“第一步”，
     *           】
     */
    @Override
    public Map<String, String> rollback3(String processInstanceId) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("流程实例processInstanceId：" + processInstanceId + "，不正确");
        }
        String currentTaskKey = task.getTaskDefinitionKey();//当前节点id
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .orderByHistoricActivityInstanceStartTime().asc()//结束时间相同时，在判断开始时间
                .list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        // ********************* 对流程进行处理，只保留之前的节点(start) ************************
        List<String> activityIds = new ArrayList<>();
        Iterator<HistoricActivityInstance> iterator = collect.iterator();
        while (iterator.hasNext()) {
            HistoricActivityInstance next = iterator.next();
            if (!activityIds.contains(next.getActivityId())) {
                activityIds.add(next.getActivityId());
            }
        }
        if (activityIds.contains(currentTaskKey)) {
            activityIds = activityIds.subList(0, activityIds.indexOf(currentTaskKey));
        }
        System.out.println("可以回退的节点 = " + activityIds);
        String targetTaskKey = "";
        if (activityIds.size() > 0) {
            targetTaskKey = activityIds.get(activityIds.size() - 1);
        } else {
            throw new BusinessRuntimeException("节点已到最初发起节点，无法在向前回退");
        }
        // ********************* 对流程进行处理，只保留之前的节点(end) ************************
        // String targetTaskKey = collect.get(0).getActivityId();//审批历史中上一个节点id
        log.info(">>>>>>>>>>>>> 任务回退：流程实例ID:{}，当前审批节点-ID:{}-NAME:{}，回退节点ID:{}-NAME:{} >>>>>>>>>>>>>",
                processInstanceId,
                task.getTaskDefinitionKey(), task.getName(),
                collect.get(0).getActivityId(),
                collect.get(0).getActivityName());
        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        Map<String, String> map = Maps.newHashMap();
        map.put("from", currentTaskKey);//从哪个id开始回退
        map.put("fromName", task.getName());//从哪个id开始回退
        map.put("to", targetTaskKey);//回退到哪个id
        String toTaskKey = targetTaskKey;
        Optional<HistoricActivityInstance> first = collect.stream().filter(it -> toTaskKey.equals(it.getActivityId())).findFirst();
        String toName = first.isPresent() ? first.get().getActivityName() : "";
        map.put("toName", toName);//回退到哪个id
        return map;
    }


    Boolean isMultiInstance(Task task) {
        Boolean isMultiInstance = false;
        String processDefinitionId = task.getProcessDefinitionId();
        String taskDefinitionKey = task.getTaskDefinitionKey();
        // 获取BPMN模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        if (bpmnModel == null) {

        }
        FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
        if (flowElement instanceof UserTask) {
            UserTask userTask = (UserTask) flowElement;
            // 检查是否配置了多实例循环特性
            MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
            if (loopCharacteristics != null) {
                isMultiInstance = true;
            }
        }
        return isMultiInstance;
    }

    Boolean isSkip(String processDefinitionId, String activityId) {
        Boolean isSkip = false;
        // 获取BPMN模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        if (bpmnModel == null) {

        }
        FlowElement flowElement = bpmnModel.getFlowElement(activityId);
        if (flowElement instanceof UserTask) {
            UserTask userTask = (UserTask) flowElement;
            // 检查当前节点是否需要跳过
            String skipExpression = userTask.getSkipExpression();
            if (StringUtil.isNotEmpty(skipExpression)) {
                isSkip = true;
            }
        }
        return isSkip;
    }


    @Override
    public Map<String, String> rollback4(String processInstanceId, String taskId) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        // Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("流程实例processInstanceId：" + processInstanceId + "，不正确");
        }
        String processDefinitionId = task.getProcessDefinitionId();//流程定义ID
        String currentTaskKey = task.getTaskDefinitionKey();//当前节点id
        Boolean multiInstance = isMultiInstance(task);
        if (multiInstance) {//不是多实例
            System.out.println("当前节点为会签节点，一旦回退，所有会签人员均回退");
        }
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .orderByHistoricActivityInstanceStartTime().asc()//结束时间相同时，在判断开始时间
                .list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        // ********************* 对流程进行处理，只保留之前的节点(start) ************************
        List<String> activityIds = new ArrayList<>();
        Iterator<HistoricActivityInstance> iterator = collect.iterator();
        while (iterator.hasNext()) {
            HistoricActivityInstance next = iterator.next();
            if (!activityIds.contains(next.getActivityId()) // 如果activityId已存在，需要过滤掉，说明这个节点回退过，存在重复
                    && !isSkip(processDefinitionId, next.getActivityId())// 如果当前节点是跳过节点，回退时也需要过滤掉，因为这个节点还会自动执行，出现回退不了的问题
            ) {
                activityIds.add(next.getActivityId());
            }
        }
        if (activityIds.contains(currentTaskKey)) {
            activityIds = activityIds.subList(0, activityIds.indexOf(currentTaskKey));
        }
        System.out.println("可以回退的节点 = " + activityIds);
        String targetTaskKey = "";
        if (activityIds.size() > 0) {
            targetTaskKey = activityIds.get(activityIds.size() - 1);
        } else {
            throw new BusinessRuntimeException("节点已到最初发起节点，无法在向前回退");
        }
        // ********************* 对流程进行处理，只保留之前的节点(end) ************************
        // String targetTaskKey = collect.get(0).getActivityId();//审批历史中上一个节点id
        log.info(">>>>>>>>>>>>> 任务回退：流程实例ID:{}，当前审批节点-ID:{}-NAME:{}，回退节点ID:{}-NAME:{} >>>>>>>>>>>>>",
                processInstanceId,
                task.getTaskDefinitionKey(), task.getName(),
                collect.get(0).getActivityId(),
                collect.get(0).getActivityName());
        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        Map<String, String> map = Maps.newHashMap();
        map.put("from", currentTaskKey);//从哪个id开始回退
        map.put("fromName", task.getName());//从哪个id开始回退
        map.put("to", targetTaskKey);//回退到哪个id
        String toTaskKey = targetTaskKey;
        Optional<HistoricActivityInstance> first = collect.stream().filter(it -> toTaskKey.equals(it.getActivityId())).findFirst();
        String toName = first.isPresent() ? first.get().getActivityName() : "";
        map.put("toName", toName);//回退到哪个id
        return map;
    }

    @Override
    public Map<String, String> rollback5(Map<String, String> map) {
        String processInstanceId = map.get("processInstanceId");
        String taskId = map.get("taskId");
        String currentTaskKey = map.get("currentTaskKey");
        String targetTaskKey = map.get("targetTaskKey");
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new BusinessRuntimeException("当前数据可能已被执行，请刷新...");
        }
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().desc().list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        log.info(">>>>>>>>>>>>> 任务回退：流程实例ID:{}，当前审批节点NAME:{}，回退节点NAME:{} >>>>>>>>>>>>>",
                processInstanceId,
                currentTaskKey,
                targetTaskKey);
        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        Map<String, String> result = Maps.newHashMap();
        map.put("from", currentTaskKey);//从哪个id开始回退
        map.put("fromName", task.getName());//从哪个id开始回退
        map.put("to", targetTaskKey);//回退到哪个id
        map.put("toName", collect.get(0).getActivityName());//回退到哪个id
        return result;
    }

    /**
     * 功能描述: 审批历史记录, 查看任务执行的详情【对应表：act_hi_actinst历史节点表】
     * @author: 庞留杰
     * @param  processInstanceId:流程实例ID
     * @return:
     * @exception
     * @date: 2020/9/16 19:38
     */
    @Override
    public Page records(PageVo pageVo, String processInstanceId, String executionId) {
        int size = pageVo.getPage_size();
        int current = pageVo.getPage_number();
        int fromIndex = (current - 1) * size;// 每页多少条
        int toIndex = current * size;// 每页多少条
        List<String> filterEvents = null;
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        if (CollectionUtil.isEmpty(filterEvents)) {
            filterEvents = Lists.newArrayList("startEvent", "endEvent", "userTask");// "sequenceFlow"
        }
        List<String> activityTypeFilter = filterEvents;
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().desc()
                .orderByHistoricActivityInstanceStartTime().desc()//结束时间相同时，在判断开始时间
                .list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());

//        下面方式有问题，通过executionId查询的历史中，多实例内部的可以查询处理，外部的内容都会过滤掉
//        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
//        historicActivityInstanceQuery.processInstanceId(processInstanceId);
//         多实例
//        if(StringUtils.isNotEmpty(executionId)){
//            historicActivityInstanceQuery.executionId(executionId);
//        }
//        List<HistoricActivityInstance> collect = historicActivityInstanceQuery.finished()//查询已经结束的记录
//                .orderByHistoricActivityInstanceEndTime().desc().list()//排序字段，方式
//                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());//过滤需要的类型


        // 走过的节点
        Map<String, String> nodes = new HashMap<>();
        for (HistoricActivityInstance his : collect) {
            nodes.put(his.getActivityId(), his.getActivityName());
        }


        int count = collect.size();
        if (toIndex > count) {
            toIndex = count;
        }
        collect = collect.subList(fromIndex, toIndex);
        // 增加备注信息
        List<Map<String, Object>> historics = Lists.newArrayList();
        //查询所有用户信息
        List<Map<String, Object>> allUsers = myService.findAllUsers();
        for (HistoricActivityInstance his : collect) {
            Map<String, Object> historic = Maps.newHashMap();
            /******** 审批历史中的字段 start *******/
            historic.put("id", his.getId());
            historic.put("activityId", his.getActivityId());
            historic.put("activityName", his.getActivityName());
            historic.put("activityType", his.getActivityType());
            historic.put("processDefinitionId", his.getProcessDefinitionId());
            historic.put("processInstanceId", his.getProcessInstanceId());
            historic.put("executionId", his.getExecutionId());
            historic.put("taskId", his.getTaskId());
            historic.put("calledProcessInstanceId", his.getCalledProcessInstanceId());
            //操作人
            Map<String, Object> map = allUsers.stream().filter(e -> (e.get("id") + "").equals(his.getAssignee())).findAny().orElse(null);
            if (map != null) {
                historic.put("assignee", his.getAssignee());//操作人ID
                historic.put("assigneeName", map.get("real_name"));//操作人
            } else {
                historic.put("assignee", his.getAssignee());//操作人ID
                historic.put("assigneeName", null);//操作人
            }
            historic.put("startTime", DateTimeUtil.format(his.getStartTime(), DateTimeUtil.DATE_TIME));
            historic.put("endTime", DateTimeUtil.format(his.getEndTime(), DateTimeUtil.DATE_TIME));
            historic.put("durationInMillis", his.getDurationInMillis());//耗时
            historic.put("deleteReason", his.getDeleteReason());//删除原因
            historic.put("tenantId", his.getTenantId());

            /**
             * 回退信息处理：
             * deleteReason:如果设置了任何删除原因，则返回此活动的删除原因(如果正常完成，则没有设置删除原因)
             *              回退原因：Change activity to sid-1
             * */
            if (StringUtil.isNotEmpty(his.getDeleteReason()) && his.getDeleteReason().contains("Change activity to ")) {
                String deleteReason = his.getDeleteReason();
                String sid = deleteReason.replace("Change activity to ", "").trim();
                String nodeName = nodes.get(sid);//回退到节点
                historic.put("rollback", "由节点【" + his.getActivityName() + "】回退至节点【" + nodeName + "】");
                historic.put("deleteReason", deleteReason);
            }
            // 多实例回退
            if (StringUtil.isNotEmpty(his.getDeleteReason()) && his.getDeleteReason().contains("Change parent activity to ")) {
                String deleteReason = his.getDeleteReason();
                String sid = deleteReason.replace("Change parent activity to ", "").trim();
                String nodeName = nodes.get(sid);//回退到节点
                historic.put("rollback", "#会签回退#由节点【" + his.getActivityName() + "】回退至节点【" + nodeName + "】");
                historic.put("deleteReason", deleteReason);
            }

            /******** 审批历史中的字段 end *******/
            //备注信息
            List<Comment> taskComments = taskService.getTaskComments(his.getTaskId());
            List<Map<String, Object>> comments = Lists.newArrayList();
            taskComments.forEach(c -> {
                Map<String, Object> comment = Maps.newHashMap();
                //操作人
                Map<String, Object> map2 = allUsers.stream().filter(e -> (e.get("id") + "").equals(c.getUserId())).findAny().orElse(null);
                comment.put("userId", c.getUserId());
                comment.put("userName", map2.get("real_name"));
                comment.put("fullMessage", c.getFullMessage());
                comment.put("time", DateTimeUtil.format(c.getTime(), DateTimeUtil.DATE_TIME));
                comments.add(comment);
            });
            historic.put("comments", comments);
            //审批历史
            historics.add(historic);
        }
        Page page = new Page(current, size, count);
        page.setRecords(historics);
        page.setTotal(count);
        return page;
    }

    @Override
    public String getTaskIdByProcessInstanceId(String processInstanceId) {
        // 支持并行网关
        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId);
        if (ContextHolderUtils.currentUser() != null) {
            String userId = ContextHolderUtils.currentUser().getId();
            taskQuery.taskCandidateOrAssigned(userId);
            String group_ids = ContextHolderUtils.getGroupIds();
            if (StringUtil.isNotEmpty(group_ids)) {
                String[] split = group_ids.split(",");
                List<String> groupIds = Arrays.asList(split);
                if (groupIds.size() > 0) {
                    taskQuery.taskCandidateGroupIn(groupIds);
                }
            }
        }
        Task task = taskQuery.active().singleResult();
        String taskId = "";
        if (task != null) {
            taskId = task.getId();
        } else {
            // 并行流程查不到是，通过这个
            task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
            if (task != null) {
                taskId = task.getId();
            }
        }
        return taskId;
    }


    /**
     * activityId：流程节点的标识。 act_de_model
     * parentExecutionId：流程执行实例标识，proInstId。
     * executionVariables：所要传入的参数。
     */
    @Override
    public Execution addMultiInstance(Map map) {
        String activityId = map.get("activityId") + "";
        String parentExecutionId = map.get("parentExecutionId") + "";
        Map<String, Object> executionVariables = Maps.newHashMap();
        /**
         * activityId：流程节点的标识。
         * parentExecutionId：流程执行实例标识，proInstId。
         * executionVariables：所要传入的参数。
         */
        executionVariables.put("assignee", "");
        Execution execution = runtimeService.addMultiInstanceExecution(activityId, parentExecutionId, executionVariables);
        System.out.println("execution=" + execution);
        return execution;
    }

    @Override
    public void deleteMultiInstance(Map map) {
        String executionId = map.get("executionId") + "";
        boolean executionIsCompleted = Boolean.valueOf(map.get("executionIsCompleted") + "");
        /**
         * executionId：
         * executionIsCompleted：定义在父多实例执行中，是否应该将删除的执行标记为已完成
         */
        runtimeService.deleteMultiInstanceExecution(executionId, executionIsCompleted);
    }

    @Override
    public boolean isCompletedByProcessInstanceId(String processInstanceId) {
        // First check if the process instance is still running
        if (isProcessInstanceEnded(processInstanceId)) {
            // If not found in RuntimeService, then check in HistoryService
            return isProcessInstanceEndedUsingHistory(processInstanceId);
        }
        // If found in RuntimeService, it's not ended
        return false;
    }


    //在 Flowable 中，根据流程实例 ID 判断流程是否结束的 API 主要依赖于 RuntimeService 和 HistoryService。下面是具体如何使用这两个服务来判断一个流程实例是否已经结束。

    /**
     * 判断流程是否结束
     * RuntimeService 用于查询当前运行中的流程实例。如果通过 RuntimeService 查询不到指定的流程实例，则可以认为该流程实例已经结束了。
     * @param processInstanceId
     * @return
     */
    public boolean isProcessInstanceEnded(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        return processInstance == null; // 如果找不到运行中的实例，说明流程已结束
    }

    /**
     * 使用历史服务判断流程是否结束
     * HistoryService 提供了访问历史数据的功能。如果你启用了历史记录（即配置了适当的历史级别），那么即使流程实例已经结束，你也可以通过 HistoryService 来查询它的状态。
     * @param processInstanceId
     * @return
     */
    public boolean isProcessInstanceEndedUsingHistory(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        return historicProcessInstance != null && historicProcessInstance.getEndTime() != null;
    }

}
